﻿using System;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Web.Hosting;
using Actya.Core.IndexDefinitions;
using Actya.Core.Infrastructure.Events;
using Actya.Core.Infrastructure.Logging;
using Actya.Core.Security;
using Autofac;
using Raven.Client;
using Raven.Client.Document;
using Raven.Client.Embedded;
using Raven.Client.Extensions;
using Raven.Client.Indexes;
using Module = Autofac.Module;

namespace Actya.Core.Infrastructure.Autofac
{
	/// <summary>
	/// Autofac module that registers all core components.
	/// </summary>
	public class CoreModule : Module
	{
		public HostSettings HostSettings { get; set; }

		protected override void Load(ContainerBuilder builder)
		{
			if (this.HostSettings == null)
			{
				throw new NullReferenceException("HostSettings may not be null.");
			}
			if (string.IsNullOrEmpty(this.HostSettings.DataDirectory) && string.IsNullOrEmpty(this.HostSettings.ConnectionString))
			{
				throw new NullReferenceException("DataDirectory and ConnectionString properties are null, so we can't setup data store context.");
			}

			// IDocumentStore
			builder.Register(c => GetDocumentStore()).As<IDocumentStore>().SingleInstance();

			// IDocumentSession
			builder.Register(c => GetDocumentSession(c.Resolve<IDocumentStore>())).As<IDocumentSession>().InstancePerLifetimeScope();

			// IEventPublisher
			builder.RegisterType<EventPublisher>().As<IEventPublisher>().InstancePerLifetimeScope();

			// Logging
			builder.RegisterType<LoggingInterceptor>();
			if (!String.IsNullOrEmpty(this.HostSettings.Logger))
			{
				// There is a logger defined in the hostsettings. Use this one instead of the null logger.
				LogManager.GetLogger = type => Activator.CreateInstance(Type.GetType(this.HostSettings.Logger), new[] { type }, null) as ILogger;
			}

			// Services with logging interceptor.
			builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
				.Where(t => t.Name.EndsWith("Service"))
				.Except<IMembershipService>()
				.AsImplementedInterfaces();
				//.EnableInterfaceInterceptors()
				//.InterceptedBy(typeof(LoggingInterceptor));

			// Event listeners
			builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
				.Where(t => t.Name.EndsWith("Listener"))
				.AsImplementedInterfaces();

			// Special membership registration.
			builder.Register(c => GetMembershipService(c.Resolve<IDocumentSession>()))
				.As<IMembershipService>();
				//.EnableInterfaceInterceptors()
				//.InterceptedBy(typeof(LoggingInterceptor));

			// Context
			builder.RegisterType<CmsContext>().As<ICmsContext>().InstancePerLifetimeScope();
		}

		private IDocumentStore GetDocumentStore()
		{
		    DocumentStore documentStore;
            if (!String.IsNullOrEmpty(this.HostSettings.DataDirectory))
            {
				documentStore = new EmbeddableDocumentStore
									{
										DataDirectory = HostingEnvironment.MapPath(this.HostSettings.DataDirectory),
										Configuration =
										{
											DefaultStorageTypeName = "munin"
										},
										UseEmbeddedHttpServer = this.HostSettings.UseHttpServerForEmbeddedDatabase
									};
            }
            else if (! String.IsNullOrEmpty(this.HostSettings.ConnectionString))
            {
                documentStore = new DocumentStore
                                    {
                                        Url = this.HostSettings.ConnectionString
                                    };
            }
            else
            {
                throw new InvalidOperationException("Can't create a document store without connectionstring or data directory.");
            }
			documentStore.Conventions.IdentityPartsSeparator = "-";
			documentStore.EnlistInDistributedTransactions = false;
		    documentStore.Initialize();

			if (!String.IsNullOrEmpty(this.HostSettings.DatabaseName))
			{
				documentStore.DatabaseCommands.EnsureDatabaseExists(this.HostSettings.DatabaseName);
				var catalog = new CompositionContainer(new AssemblyCatalog(typeof(Pages_BySearchTerm).Assembly));
				IndexCreation.CreateIndexes(catalog, documentStore.DatabaseCommands.ForDatabase(this.HostSettings.DatabaseName), documentStore.Conventions);
			}
			else
			{
				// Index creation
				IndexCreation.CreateIndexes(typeof(Pages_BySearchTerm).Assembly, documentStore);				
			}
			
			return documentStore;
		}

		private IDocumentSession GetDocumentSession(IDocumentStore documentStore)
		{
			return !String.IsNullOrEmpty(this.HostSettings.DatabaseName)
			       	? documentStore.OpenSession(this.HostSettings.DatabaseName)
			       	: documentStore.OpenSession();
		}

		private IMembershipService GetMembershipService(IDocumentSession documentSession)
		{
			if (this.HostSettings.MembershipType == MembershipType.Membership)
			{
				return new AspnetMembershipService(this.HostSettings.MembershipProvider, this.HostSettings.RoleProvider);
			}
			return new DefaultMembershipService(documentSession);
		}
	}
}
